package evocarcassonne.core.abstractors;

import evocarcassonne.core.board.Tile;
import evocarcassonne.core.board.locations.Zone;
import evocarcassonne.core.utils.Card;
import evocarcassonne.core.utils.ZoneType;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public abstract class AbstractTileDrawer {

    Set<Zone> alreadyDoneTiles;

    public void draw(Tile tile) {

        if (tile == null) {
            return;
        }

        // MEADOW
        drawMeadow();

        alreadyDoneTiles = new HashSet();

        Zone[] zones = new Zone[4];
        zones[0] = tile.getZone(Card.N);
        zones[1] = tile.getZone(Card.E);
        zones[2] = tile.getZone(Card.S);
        zones[3] = tile.getZone(Card.W);

        for (int i = 0; i < zones.length; i++) {
            Zone zone = zones[i];
            if (!alreadyDoneTiles.contains(zone)) {
                if (zone.getType() == ZoneType.CITY) {
                    drawCity(zone);
                } else if (zone.getType() == ZoneType.ROAD) {
                    drawRoad(zone);
                }
            }
        }
    }

    private void drawRoad(Zone zone) {
        if (zone.isDeadEnd()) {
            drawDeadEnd(zone.card);

        } else if (zone.getTouch() != -1) {
            drawTouchRoad(zone.card, zone.getTouch());

        } else if (zone.getLinks().isEmpty()) {
            drawSimpleRoad(zone.card);

        } else if (zone.getLinks().size() == 1) {

            int refCard = zone.card;
            int foreignCard = zone.getLinks().iterator().next().card;
            int diff = Math.abs(foreignCard - refCard);

            if (diff == 3) {
                drawSideRoad(refCard);

            } else if (diff == 6) {
                drawOppositeRoad(refCard);

            } else if (diff == 9) {
                drawSideRoad(foreignCard);
            }

        } else if (zone.getLinks().size() == 2) {
            Iterator<Zone> iter = zone.getLinks().iterator();

            int sum = zone.card + iter.next().card + iter.next().card;

            if (sum == 12) {
                drawTripleRoad(Card.N);
            } else if (sum == 15) {
                drawTripleRoad(Card.W);
            } else if (sum == 18) {
                drawTripleRoad(Card.S);
            } else if (sum == 21) {
                drawTripleRoad(Card.E);
            }
        } else if (zone.getLinks().size() == 3) {
            drawQuadRoad();
        }

        alreadyDoneTiles.addAll(zone.getLinks());
    }

    private void drawCity(Zone zone) {

        if (zone.getLinks().isEmpty()) {
            drawSimpleCity(zone.card);

        } else if (zone.getLinks().size() == 1) {

            int refCard = zone.card;
            int foreignCard = zone.getLinks().iterator().next().card;
            int diff = Math.abs(foreignCard - refCard);

            if (diff == 3) {
                drawDoubleCity(refCard);

            } else if (diff == 6) {
                drawOppositeCity(refCard);

            } else if (diff == 9) {
                drawDoubleCity(foreignCard);
            }

        } else if (zone.getLinks().size() == 2) {

            Iterator<Zone> iter = zone.getLinks().iterator();

            int sum = zone.card + iter.next().card + iter.next().card;

            if (sum == 12) {
                drawTripleCity(Card.N);
            } else if (sum == 15) {
                drawTripleCity(Card.W);
            } else if (sum == 18) {
                drawTripleCity(Card.S);
            } else if (sum == 21) {
                drawTripleCity(Card.E);
            }
        } else if (zone.getLinks().size() == 3) {
            drawQuadCity();
        }
        alreadyDoneTiles.addAll(zone.getLinks());
    }

    private void drawOppositeRoad(int card) {
        AbstractDrawableTile drawable = _generateOppositeRoadGraphics();
        drawTile(card, drawable);
    }

    private void drawSideRoad(int card) {
        AbstractDrawableTile drawable = _generateSideRoadGraphics();
        drawTile(card, drawable);
    }

    private void drawSimpleRoad(int card) {
        AbstractDrawableTile drawable = _generateSimpleRoadGraphics();
        drawTile(card, drawable);
    }

    private void drawTouchRoad(int card, int touch) {
        AbstractDrawableTile drawable = _generateTouchRoadGraphics();

        // todo cherche l'orientation du touch...
        // faire comme pour dif = carfref - foreigncard ??

        drawTile(card, drawable);
    }

    private void drawTripleRoad(int card) {
        AbstractDrawableTile drawable = _generateTripleRoadGraphics();
        drawTile(card, drawable);
    }

    private void drawQuadRoad() {
        AbstractDrawableTile drawable = _generateQuadRoadGraphics();
        drawTile(Card.N, drawable);
    }

    private void drawQuadCity() {
        AbstractDrawableTile drawable = _generateQuadCityGraphics();
        drawTile(Card.N, drawable);
    }

    private void drawTripleCity(int card) {
        AbstractDrawableTile drawable = _generateTripleCityGraphics();
        drawTile(card, drawable);
    }

    private void drawOppositeCity(int card) {
        AbstractDrawableTile drawable = _generateOppositeCityGraphics();
        drawTile(card, drawable);
    }

    private void drawMeadow() {
        AbstractDrawableTile drawable = _generateMeadowGraphics();
        drawTile(Card.N, drawable);
    }

    private void drawDoubleCity(int card) {
        AbstractDrawableTile drawable = _generateSideCityGraphics();
        drawTile(card, drawable);
    }

    private void drawSimpleCity(int card) {
        AbstractDrawableTile drawable = _generateSimpleCityGraphics();
        drawTile(card, drawable);
    }

    private void drawDeadEnd(int card) {
        AbstractDrawableTile drawable = _generateDeadEndGraphics();
        drawTile(card, drawable);
    }

    private void drawTile(int mainCard, AbstractDrawableTile image) {

        switch (mainCard) {

            case Card.N:
                _draw0(image);
                break;

            case Card.E:
                _draw90(image);
                break;

            case Card.S:
                _draw180(image);
                break;

            case Card.W:
                _draw270(image);
                break;
        }
    }

// ABSTRACTS METHODS
    protected abstract AbstractDrawableTile _generateMeadowGraphics();

    // roads
    protected abstract AbstractDrawableTile _generateSimpleRoadGraphics();

    protected abstract AbstractDrawableTile _generateSideRoadGraphics();

    protected abstract AbstractDrawableTile _generateOppositeRoadGraphics();

    protected abstract AbstractDrawableTile _generateTouchRoadGraphics();

    protected abstract AbstractDrawableTile _generateDeadEndGraphics();

    protected abstract AbstractDrawableTile _generateTripleRoadGraphics();

    protected abstract AbstractDrawableTile _generateQuadRoadGraphics();

    // cities
    protected abstract AbstractDrawableTile _generateSideCityGraphics();

    protected abstract AbstractDrawableTile _generateSimpleCityGraphics();

    protected abstract AbstractDrawableTile _generateOppositeCityGraphics();

    protected abstract AbstractDrawableTile _generateTripleCityGraphics();

    protected abstract AbstractDrawableTile _generateQuadCityGraphics();

    // rotations
    protected abstract void _draw90(AbstractDrawableTile image);

    protected abstract void _draw180(AbstractDrawableTile image);

    protected abstract void _draw270(AbstractDrawableTile image);

    protected abstract void _draw0(AbstractDrawableTile image);
}
